# Semi-manual registration for vBulletin
# by Cee Pee
# Note that human verification will always have to be input by the bot owner.

from deps.Form import Form
import forumapi
import sys
import os

class RegistrationException(Exception):
	"""Indicates an internal problem."""

class RegistrationError(RegistrationException):
	"""Indicates a server-side error during the registration process."""

class StopRegistration(RegistrationException):
	"""Indicates a client-side error during the registration process."""

class Registration(object):
	"""Handles the registration of a new account."""
	
	def __init__(self, site, args):
		self.site = site
		
		# Guest session
		self.session = forumapi.Session(self.site)
		
		self.name = args["name"]
		self.password = args["password"]
		self.email = args["email"]
		
		# Cloak user agent (is this bad?)
		forumapi.PageRequest.user_agent = "Mozilla/5.0 (Linux; U; en-US; rv:1.9.0.7) Firefox/3.0.7"
		
		# Send referers
		forumapi.PageRequest.send_referer = True
		
		self.hv_type = args["type"]
		self.timezone = args["timezone"]
		self.show_rules = args["show_rules"]
	
	def accept_rules(self):
		"""Returns post data to use for accepting the rules."""
		
		page = self.session.get_page("register.php")
		page_soup = self.session.soup(page)
		soup_form = page_soup.find("form", action=lambda url: "do=register" in url)

		if not soup_form:
			raise(forumapi.PermissionError(self.session.get_error(page_soup) or "Could not find rules."))
		
		if self.show_rules:
			rules = soup_form.find("div", "page").findAll("p")
			for rule in rules:
				print(forumapi.remove_tags(rule))
			
			answer = input("Do you agree to these rules? (Y/N) ")
			if answer is not "Y":
				raise(StopRegistration("Registration halted by user."))
		
		accept_data = Form(soup_form, session=self.session)
		accept_data["agree"] = "1"
		return(accept_data)
	
	def prepare_data(self, accept_form):
		"""Returns post data to use for the registration."""
		
		page = accept_form.submit()
		page_soup = self.session.soup(page)
		soup_form = page_soup.find("form", action=lambda url: "do=addmember" in url)

		if not soup_form:
			raise(forumapi.PermissionError(self.session.get_error(page_soup) or "Could not find registration form."))
		
		form_data = Form(soup_form, session=self.session)
		# TODO: Turn this into a `for data in form_data` to make it easier to
		# spot required fields not addressed, and maybe to spot HV type
		
		form_data["username"] = self.name
		form_data["password"] = self.password
		form_data["passwordconfirm"] = self.password
		form_data["email"] = self.email
		form_data["emailconfirm"] = self.email
		form_data["timezoneoffset"] = str(self.timezone)
		form_data["dst"] = "2"
		
		# Handle human verification by having the bot owner answer them
		if self.hv_type == "default":
			image = soup_form.find("img", id="imagereg")
			imgattrs = dict(image.attrs)
			
			answer = self.image(self.site.url + imgattrs["src"])
			
			form_data["humanverify[input]"] = answer
		elif self.hv_type == "recaptcha":
			iframe = soup_form.find("iframe")
			frameattrs = dict(iframe.attrs)
			
			verify_code = self.recaptcha(frameattrs["src"])
			
			form_data["recaptcha_challenge_field"] = verify_code
			form_data["recaptcha_response_field"] = "manual_challenge"
		elif self.hv_type == "question":
			answer = self.question(soup_form)
			form_data["humanverify[input]"] = answer
		
		return(form_data)
	
	def image(self, url):
		"""Handles the request, opening and solving of a CAPTCHA image."""
		
		imgdata = forumapi.PageRequest(url, encoding=self.site.encoding, headers=self.headers)
		
		f = open("tmp.jpg", mode="wb")
		f.write(bytes(imgdata.data, self.site.encoding))
		f.close()
		
		# TODO: Is there a consistent way to open images on all OSes?
		if sys.platform == "win32":
			print("The CAPTCHA file will be opened. Please input the text.")
			os.startfile("tmp.jpg")
		else:
			print("Please open the file and input the text.")
		
		answer = input("Answer: ")
		return(answer)
	
	def recaptcha(self, url):
		"""Handles a reCAPTCHA challenge."""
		
		site = "http://api.recaptcha.net"
		request = forumapi.PageRequest(url, encoding=self.site.encoding, headers=self.headers)
		soup = self.session.soup(request)
		img = soup.find("img", id="recaptcha_image")
		attrs = dict(img.attrs)
		
		# Use the normal image answering system
		answer = self.image(site + img["src"])
		form_soup = soup.find("form")
		form_data = Form(form_soup, url=url)
		
		form_data["recaptcha_response_field"] = answer
		
		response = form_data.submit()
		verify_soup = self.session.soup(response)
		verify_code = verify_soup.find("textarea").string
		if verify_code is None:
			return(None)
		else:
			return(str(verify_code))
	
	def question(self, soup):
		"""Handles a question and answer challenge."""
		
		fieldset = soup.findAll("fieldset")[2]
		question = fieldset.find("label")
		print("Question: " + str(question.string))
		answer = input("Answer: ")
		return(answer)
	
	def register(self, form_data):
		"""Given post data, finishes the registration procedure."""
		
		page = form_data.submit()
		soup = self.session.soup(page)
		
		cell = soup.find("td", "alt1")
		if cell:
			print(cell.find("li").string)
		
		print("Looks like we have registered! Cleaning up...")
		if self.type == "default" or self.type == "recaptcha":
			os.remove("tmp.jpg")
	
	def verify_mail(self, url):
		"""Given a verification URI, verifies the mail."""
		
		page = self.session.get_page(url)
		
		# TODO: Display something here?
